
// Encoder.java  canok
package com.example.myapplication;
 
import android.media.MediaCodec;
import android.media.MediaCodecInfo;
import android.media.MediaFormat;
import android.os.Bundle;
import android.util.Log;
 
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.nio.ByteBuffer;
 
public class Encoder {
        //同步编码
        private static final String TAG = "Encoder";
        //https://blog.csdn.net/u011913612/article/details/68943010
        MediaCodec mediaCodec;
        private boolean bRun = true;
        private FileInputStream fileYUVSrcStream;
        private FileOutputStream fileOutputStream;
        private  final int mWidth;
        private  final int mHeight;
        private final int mFrameRate;
        private boolean forhonor=true;
        public Encoder(int w, int h, int fps) {
            mWidth = w;
            mHeight = h;
            mFrameRate = fps;
        }
 
        public void start(String yuvfilein,String h264fileout) {
            Log.d(TAG, "start: "+yuvfilein+" out:"+h264fileout);
            bRun = true;
            new Thread(new Runnable() {
                @Override
                public void run() {
                    int framelen = mWidth*mHeight*3/2;
                    byte[] frame = new byte[framelen];
                    int inCount =0;
                    int outCount=0;
                    int resettime =0;
                    try {
                        fileOutputStream = new FileOutputStream(h264fileout, false);
                    } catch (FileNotFoundException e) {
                        Log.d(TAG, "run: cannot create file!");
                        e.printStackTrace();
                        return;
                    }
                    try {
                        fileYUVSrcStream = new FileInputStream(yuvfilein);
                        try {
                            fileYUVSrcStream.mark(fileYUVSrcStream.available());
                        } catch (IOException e) {
                            e.printStackTrace();
                            return;
                        }
                    } catch (FileNotFoundException e) {
                        e.printStackTrace();
                        return;
                    }
 
                    try {
                        mediaCodec = MediaCodec.createEncoderByType("video/avc");
                    } catch (IOException e) {
                        e.printStackTrace();
                    }
                    if(mediaCodec == null){
                        Log.d(TAG, "run: err create!");
                        return;
                    }
 
 
                    MediaFormat inputMediaFormat = MediaFormat.createVideoFormat("video/avc", mWidth, mHeight);
                    inputMediaFormat.setInteger(MediaFormat.KEY_BIT_RATE, 500*1000);
                    inputMediaFormat.setInteger(MediaFormat.KEY_FRAME_RATE, mFrameRate);
                    inputMediaFormat.setInteger(MediaFormat.KEY_I_FRAME_INTERVAL, 5);//单位为 秒
 
                    //inputMediaFormat.setInteger(MediaFormat.KEY_COLOR_FORMAT, MediaCodecInfo.CodecCapabilities.COLOR_FormatYUV420PackedPlanar);
                    if(forhonor) {
                        //不支持？？ 荣耀 老手机，Acodec报错
                        // inputMediaFormat.setInteger(MediaFormat.KEY_PREPEND_HEADER_TO_SYNC_FRAMES,1);
                        inputMediaFormat.setInteger(MediaFormat.KEY_COLOR_FORMAT, MediaCodecInfo.CodecCapabilities.COLOR_FormatYUV420Flexible);
                    }
                    else {
                         inputMediaFormat.setInteger(MediaFormat.KEY_COLOR_FORMAT, MediaCodecInfo.CodecCapabilities.COLOR_QCOM_FormatYUV420SemiPlanar);
                        // inputMediaFormat.setInteger(MediaFormat.KEY_COLOR_FORMAT, MediaCodecInfo.CodecCapabilities.COLOR_FormatYUV420Planar);
                        inputMediaFormat.setInteger(MediaFormat.KEY_PREPEND_HEADER_TO_SYNC_FRAMES,1);
                    }
 
 
                    mediaCodec.configure(inputMediaFormat, null, null, MediaCodec.CONFIGURE_FLAG_ENCODE);
                    mediaCodec.start();
                    while (bRun) {
                        //无限超时
                        int inputBufferIndex = mediaCodec.dequeueInputBuffer(-1);
                        if (inputBufferIndex >= 0) {
                            ByteBuffer buffer = mediaCodec.getInputBuffer(inputBufferIndex);
                            try {
                                if(fileYUVSrcStream.available()<framelen){
                                    Log.d(TAG, "reset[][][][][][][][]");
                                   // fileYUVSrcStream.reset(); 报错
                                    fileYUVSrcStream.close();
                                    fileYUVSrcStream = new FileInputStream(yuvfilein); //干脆重新打开
                                    if(0==resettime) {
                                        changeBitRate(100);
                                        changeFrameRate(mFrameRate * 3);
                                        fileOutputStream.close();
                                        fileOutputStream = new FileOutputStream(h264fileout + (resettime++), false);
                                    }
                                }
                            } catch (IOException e) {
                                e.printStackTrace();
                            }
 
                            try {
                                fileYUVSrcStream.read(frame);
                            } catch (IOException e) {
                                e.printStackTrace();
//                                    if(e instanceof EOFException){
//                                        try {
//                                            Log.d(TAG, "reset[][][][][][][][]");
//                                            fileYUVSrcStream.reset();
//                                        } catch (IOException ioException) {
//                                            ioException.printStackTrace();
//                                        }
//                                    }
                            }
                            buffer.clear();
                            buffer.limit(frame.length);
                            buffer.put(frame, 0, frame.length);
                            //入队列，放入数据
                            Log.d(TAG, "in<<<<<<<"+(inCount++));
                            mediaCodec.queueInputBuffer(inputBufferIndex, 0, frame.length, 0, MediaCodec.BUFFER_FLAG_CODEC_CONFIG);
 
                            //取编码后的数据, 无限超时， 这里采取入一帧出一帧的模式。
                            MediaCodec.BufferInfo mBufferInfo = new MediaCodec.BufferInfo();
                            int outIndex = mediaCodec.dequeueOutputBuffer(mBufferInfo, -1);
                            if(outIndex == MediaCodec.INFO_OUTPUT_FORMAT_CHANGED){
//                                //头一次读，到这里 竟然报错空指针？
//                                    MediaFormat outformat = mediaCodec.getOutputFormat();
//                                    int bitrate = outformat.getInteger(MediaFormat.KEY_BIT_RATE);
//                                    int w = outformat.getInteger(MediaFormat.KEY_WIDTH);
//                                    int h = outformat.getInteger(MediaFormat.KEY_HEIGHT);
//                                    int framrate = outformat.getInteger(MediaFormat.KEY_FRAME_RATE);
//                                    int iInternal = outformat.getInteger(MediaFormat.KEY_I_FRAME_INTERVAL);
//                                    Log.d(TAG, "INFO_OUTPUT_FORMAT_CHANGED: " + w + "X" + h + "@" + framrate + "|" + iInternal);
 
                                //继续读
                                outIndex = mediaCodec.dequeueOutputBuffer(mBufferInfo, -1);
                            }else if(outIndex == MediaCodec.INFO_OUTPUT_BUFFERS_CHANGED){
                                Log.d(TAG, "INFO_OUTPUT_BUFFERS_CHANGED ");
                            }
 
 
                            if(outIndex >= 0) {
                                ByteBuffer outputBuffer = mediaCodec.getOutputBuffer(outIndex);
                                byte[] bb = new byte[mBufferInfo.size];
                                outputBuffer.get(bb);
                                try {
                                    Log.d(TAG, "out>>>>>"+(outCount++)+"isKeyFram "+(mBufferInfo.flags== MediaCodec.BUFFER_FLAG_KEY_FRAME));
                                    fileOutputStream.write(bb);
                                } catch (IOException e) {
                                    e.printStackTrace();
                                }
 
                                //头一次读，到这里 竟然报错空指针？
//                                    MediaFormat outformat = mediaCodec.getOutputFormat(outIndex);
//                                    int bitrate = outformat.getInteger(MediaFormat.KEY_BIT_RATE);
//                                    int w = outformat.getInteger(MediaFormat.KEY_WIDTH);
//                                    int h = outformat.getInteger(MediaFormat.KEY_HEIGHT);
//                                    int framrate = outformat.getInteger(MediaFormat.KEY_FRAME_RATE);
//                                    int iInternal = outformat.getInteger(MediaFormat.KEY_I_FRAME_INTERVAL);
//                                    Log.d(TAG, "INFO_OUTPUT_FORMAT_CHANGED: " + w + "X" + h + "@" + framrate + "|" + iInternal);
 
                                mediaCodec.releaseOutputBuffer(outIndex, false);
                            }else {
                                Log.d(TAG, "run: outindex "+outIndex);
                            }
                        } else {
                            Log.d(TAG, "run: inputBufferindex:"+inputBufferIndex);
                        }
 
                    }
                    mediaCodec.stop();
                    mediaCodec.release();
                    mediaCodec = null;
                    if( fileOutputStream!=null) {
                        try {
                            fileOutputStream.close();
                        } catch (IOException e) {
                            e.printStackTrace();
                        }
                    }
                    if( fileYUVSrcStream!=null) {
                        try {
                            fileYUVSrcStream.close();
                        } catch (IOException e) {
                            e.printStackTrace();
                        }
                    }
                }
            }).start();
 
        }
 
        public void stop() {
            bRun = false;
        }
 
 
        public void changeBitRate(int bitrate){
            if(mediaCodec!=null){
               Bundle param =  new Bundle();
               param.putInt( MediaFormat.KEY_BIT_RATE,bitrate);
                Log.d(TAG, "changeBitRate: "+bitrate);
               mediaCodec.setParameters(param);
            }
        }
 
    public void changeFrameRate(int frameRate){
        if(mediaCodec!=null){
            Bundle param =  new Bundle();
            param.putInt( MediaFormat.KEY_FRAME_RATE,frameRate);
            Log.d(TAG, "changeFrameRate: "+frameRate);
            mediaCodec.setParameters(param);
        }
    }
}


